package org.example.config.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.controller.HttpResult;
import org.example.util.jwt.JWTUtil;
import org.example.util.jwt.ParseJWTException;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.Key;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class JWTFilter extends OncePerRequestFilter {
    private Key key;
    private ObjectMapper objectMapper;

    public JWTFilter(Key key) {
        this.key = key;
        objectMapper = new ObjectMapper();
    }

    private void sendAuthFailure(HttpServletRequest request, HttpServletResponse response) {
        response.setContentType("application/json");
        response.setCharacterEncoding("utf8");

        HttpResult httpResult = HttpResult.error("解析token出错");
        try (PrintWriter writer = response.getWriter()) {
            writer.write(objectMapper.writeValueAsString(httpResult));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (authHeader != null) {
            String token = authHeader.replace("Bearer ", "");
            try {
                Map<String, Object> parsedClaims = JWTUtil.parseToken(token, key);
                String username = (String) parsedClaims.get("username");
                List<GrantedAuthority> roles = ((ArrayList<String>) parsedClaims.get("roles"))
                        .stream().map(role -> {
                            return new SimpleGrantedAuthority("ROLE_" + role);
                        }).collect(Collectors.toList());
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, token, roles);
                usernamePasswordAuthenticationToken.setDetails(parsedClaims);
                SecurityContextHolder.clearContext();
                SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
                securityContext.setAuthentication(usernamePasswordAuthenticationToken);
                SecurityContextHolder.setContext(securityContext);
            } catch (ParseJWTException parseJWTException) {
                sendAuthFailure(request, response);
                return;
            }
        }
        filterChain.doFilter(request,response);
    }
}
